﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using Krile.Data;
using Krile.Kernel.Control.Surface;
using System.Windows.Forms;
using Krile.Kernel.Data;
using Krile.Forms.MainForm.Controls.XTab;

namespace Krile.Forms.MainForm.Controls.Surface
{
    internal static class ServiceColorExtension
    {
        public static Color GetServiceColor(this Service svc)
        {
            var svclr = Core.Config.PluginCommon.GetServiceColor(svc.Id);
            if (svclr == null)
                return svc.ServiceColor;
            else
                return svclr.ServiceBackColor.Value;
        }

        public static Color GetServiceForeColor(this Service svc)
        {
            var svclr = Core.Config.PluginCommon.GetServiceColor(svc.Id);
            if (svclr == null)
                return svc.ForeColor;
            else
                return svclr.ServiceForeColor.Value;
        }

        public static Color GetServiceSecondaryColor(this Service svc)
        {
            var svclr = Core.Config.PluginCommon.GetServiceColor(svc.Id);
            if (svclr == null)
                return svc.SecondaryColor;
            else
                return svclr.ServiceSecondaryColor.Value;
        }

    }

    /// <summary>
    /// Dirty rendering control is here. Warning!
    /// </summary>
    internal class SurfaceRenderer
    {
        internal bool drawTopDatetTime = false;

        CompositionManager compman;

        internal IEnumerable<IMouseControllable> GetMouseControllables()
        {
            var pf = parent.GetParentForm();
            lock (soplocker)
            {
                if (sopclusters.Count > 0)
                    foreach (var c in sopclusters)
                    {
                        c.ParentForm = pf;
                        yield return c;
                    }
                if (parent.SelectedStatus != null && parent.SelectedStatus.BaseStatus.Commands != null)
                    foreach (var c in parent.SelectedStatus.BaseStatus.Commands)
                    {
                        c.ParentForm = pf;
                        yield return c;
                    }
            }
            selectedMouseControllable.ParentForm = pf;
            yield return selectedMouseControllable;
            baseMouseControllable.ParentForm = pf;
            yield return baseMouseControllable;
        }

        IMouseControllable baseMouseControllable;

        internal class BaseController : IMouseControllable
        {
            public Krile.Kernel.Bridges.IForm ParentForm { get; set; }

            SurfaceCore parent;
            SurfaceRenderer render;
            internal BaseController(SurfaceCore parent, SurfaceRenderer render)
            {
                this.parent = parent;
                this.render = render;
            }

            string helpTip = null;
            public string HelptipText
            {
                get { return helpTip; }
            }

            public bool HitTest(Point position)
            {
                long cpi = 0;
                if ((cpi = curPointingIndex(position.Y)) >= 0 &&
                    parent.Statuses != null && parent.Statuses.Length > cpi)
                {
                    Status target = parent.Statuses[cpi].BaseStatus;
                    StringBuilder sb = new StringBuilder();
                    if (target.User != null)
                    {
                        if (target.User.Name != null)
                        {
                            sb.Append(target.User.Name);
                            sb.Append(" : ");
                        }
                        else if (target.User.Id != null)
                        {
                            sb.Append(target.User.Id);
                            sb.Append(" : ");
                        }
                        else
                        {
                            throw new KrileStrictCheckException("Status.User.Id is NULL reference. this property must set.");
                        }
                    }
                    else
                    {
                        throw new KrileStrictCheckException("Status.User is NULL reference. this property must set.");
                    }
                    if (target.Text == null)
                        throw new KrileStrictCheckException("Status.Text is NULL reference. this property must set.");

                    if (target.Text.Original == null)
                        throw new KrileStrictCheckException("Status.Text.Original is NULL reference. this property must set.");

                    if (!String.IsNullOrEmpty(target.Text.HeaderText))
                        sb.Append("[" + target.Text.HeaderText + "]");
                    string targetText = target.Text.Original;
                    while (targetText != String.Empty)
                    {
                        if (targetText.Length > Core.Config.Appearance.Renderer.TooltipNewlineCount)
                        {
                            sb.Append(targetText.Substring(0, Core.Config.Appearance.Renderer.TooltipNewlineCount));
                            targetText = targetText.Substring(Core.Config.Appearance.Renderer.TooltipNewlineCount);
                            sb.Append(Environment.NewLine);
                        }
                        else
                        {
                            sb.Append(targetText);
                            targetText = String.Empty;
                        }
                    }
                    if (!String.IsNullOrEmpty(target.Text.FooterText))
                        sb.Append("[" + target.Text.FooterText + "]");
                    helpTip = sb.ToString();
                }
                else
                {
                    helpTip = null;
                }
                return true;
            }

            public void OnMouseClick(object sender, ChainMouseEventArgs e)
            {
                if (helpTip == null)
                {
                    e.SetThrowSub();
                    return;
                }
                long cpi = -1;
                if ((cpi = curPointingIndex(e.Y)) >= 0 &&
                    parent.Statuses != null && parent.Statuses.Length > cpi)
                    parent.SelectedIndex = cpi;
            }

            public long curPointingIndex(long yposition)
            {
                yposition += parent.VerticalPosition;
                int idx = (int)Math.Floor((double)yposition / render.NormalItemHeight);
                //通常アイテム高さで概算した範囲内に選択中インデックスが存在していたら別処理
                if (parent.SelectedIndex >= 0 && parent.SelectedIndex <= idx)
                {
                    //通常アイテムの頭位置までを計算
                    long syp = render.NormalItemHeight * parent.SelectedIndex;
                    if (yposition >= syp && yposition <= syp + render.SelectedItemHeight)
                    {
                        //選択中のステータス領域をポイントしている
                        return -1;
                    }
                    //それよりも下
                    yposition -= syp + render.SelectedItemHeight;
                    //(残りの描画領域 / アイテム一つあたりの高さ) + 選択アイテムの位置 + 1(選択アイテムのカウント分)
                    long ret = (int)Math.Floor((double)yposition / render.NormalItemHeight) + parent.SelectedIndex + 1;
                    return ret;
                }
                else
                {
                    //選択インデックスはもっと下、もしくは選択していない
                    //だから普通に割る
                    return idx;
                }
            }

            public void OnMouseDoubleClick(object sender, ChainMouseEventArgs e)
            {
                this.OnMouseClick(sender, e);
            }

            public void OnMouseDown(object sender, ChainMouseEventArgs e)
            {
                e.SetThrowSub();
            }

            public void OnMouseEnter(object sender, ChainMouseEventArgs e)
            {
                e.SetThrowSub();
            }

            public void OnMouseLeave(object sender, ChainMouseEventArgs e)
            {
                e.SetThrowSub();
            }

            public void OnMouseMove(object sender, ChainMouseEventArgs e)
            {
                if (Core.Config.Behavior.PointingNameToSelectStatus && e.X <= Core.Config.Appearance.Renderer.UserNameArea)
                {
                    this.parent.FindForm().Activate();
                    this.OnMouseClick(sender, e);
                }
                else
                {
                    e.SetThrowSub();
                }
            }

            public void OnMouseOver(object sender, ChainMouseEventArgs e)
            {
                e.SetThrowSub();
            }

            public void OnMouseUp(object sender, ChainMouseEventArgs e)
            {
                e.SetThrowSub();
            }
        }

        IMouseControllable selectedMouseControllable;

        internal class SelectedBase : IMouseControllable
        {
            public Krile.Kernel.Bridges.IForm ParentForm { get; set; }

            SurfaceCore parent;
            SurfaceRenderer render;
            internal SelectedBase(SurfaceCore parent, SurfaceRenderer render)
            {
                this.parent = parent;
                this.render = render;
            }

            public string HelptipText { get { return null; } }

            public bool HitTest(Point position)
            {
                if (parent.SelectedIndex == -1)
                    return false;
                long sp = parent.SelectedIndex * render.NormalItemHeight - parent.VerticalPosition;
                long ep = sp + render.SelectedItemHeight;
                return sp <= position.Y && position.Y <= ep;
            }

            ContextMenuStrip cms = null;
            public void OnMouseClick(object sender, ChainMouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                {
                    if (Core.Config.Behavior.ClickSelectedStatusToDeselect)
                    {
                        var parn = parent.GetParentForm();
                        if (parn != null)
                            parn.DeselectStatus();
                    }
                }
                else if (e.Button == MouseButtons.Right)
                {
                    //show context menu
                    cms = parent.CreateContextMenu();
                    cms.Show(parent, e.Location);
                }
            }

            public void OnMouseDoubleClick(object sender, ChainMouseEventArgs e)
            {
                this.OnMouseClick(sender, e);
            }

            public void OnMouseDown(object sender, ChainMouseEventArgs e) { }

            public void OnMouseEnter(object sender, ChainMouseEventArgs e) { }

            public void OnMouseLeave(object sender, ChainMouseEventArgs e) { }

            public void OnMouseMove(object sender, ChainMouseEventArgs e) { }

            public void OnMouseOver(object sender, ChainMouseEventArgs e) { }

            public void OnMouseUp(object sender, ChainMouseEventArgs e) { }
        }

        private SurfaceCore parent;
        internal SurfaceRenderer(SurfaceCore parent)
        {
            compman = new CompositionManager();
            this.parent = parent;
            baseMouseControllable = new BaseController(parent, this);
            selectedMouseControllable = new SelectedBase(parent, this);
        }

        internal int NormalItemHeight
        {
            get
            {
                if (Core.Config != null)
                    return Core.Config.Appearance.Renderer.Detail.NormalListHeight;
                else
                    return 0;
            }
        }

        int _selItemHeight = 0;
        internal int SelectedItemHeight
        {
            get { return _selItemHeight; }
            private set { _selItemHeight = value; }
        }

        internal void UpdateComposite()
        {
            if (Core.Config != null)
            {
                if (parent.SelectedStatus == null)
                {
                    compman.ReleaseComposite();
                    return;
                }
                compman.ChangeCompositeTarget(parent.SelectedStatus.BaseStatus.Text.MessageObjects);
                if (Core.Config.Appearance.Renderer.ShowMainIcon)
                    compman.UpdateComposition(parent.Width - Core.Config.Appearance.Renderer.UserNameArea - 2 - (Core.Config.Appearance.Renderer.Detail.TextRenderingMargin * 2));
                else
                    compman.UpdateComposition(parent.Width - (Core.Config.Appearance.Renderer.Detail.TextRenderingMargin * 2));
                if (parent.SelectedStatus.BaseStatus.ReplyData != null)
                    SelectedItemHeight =
                        compman.CompositedHeight +
                        Core.Config.Appearance.Renderer.Detail.NormalListHeight +
                        Core.Config.Advanced.SelectedListNameCommandHeight + 2;
                else
                    SelectedItemHeight = compman.CompositedHeight + Core.Config.Advanced.SelectedListNameCommandHeight + 2;
                if (SelectedItemHeight < Core.Config.Appearance.Renderer.Detail.SelectedListMinHeight - 2)
                    SelectedItemHeight = Core.Config.Appearance.Renderer.Detail.SelectedListMinHeight;
                SelectedItemHeight += Core.Config.Appearance.Renderer.Detail.TextRenderingMargin * 2;
            }
        }

        internal void Draw(Graphics g, int yoffset)
        {
            try
            {
                lock (parent.statusesLocker)
                {
                    lock (soplocker)
                    {
                        sopclusters.Clear();
                    }
                    int ctor = 0;
                    int cpos = 0;
                    if (parent.Statuses != null)
                    {
                        int startpoint = -1;
                        int endpoint = -1;
                        while (ctor < parent.Statuses.Length)
                        {
                            try
                            {
                                int bp = cpos;
                                if (parent.SelectedIndex == ctor)
                                    bp += SelectedItemHeight;
                                else
                                    bp += NormalItemHeight;
                                if (bp > yoffset)
                                {
                                    if (parent.SelectedIndex == ctor)
                                        DrawSelectedItem(parent.Statuses[ctor], g, cpos - yoffset);
                                    else
                                        DrawNormalItem(parent.Statuses[ctor], ctor, g, cpos - yoffset);
                                    if (drawTopDatetTime && cpos <= yoffset)
                                    {
                                        startpoint = ctor;
                                    }
                                }
                                cpos = bp;
                                endpoint = ctor;
                                if (cpos >= yoffset + parent.Height)
                                    break;
                                ctor++;
                            }
                            catch (Exception e)
                            {
                                Subsystem.Debugger.AddReport(
                                    new Krile.Kernel.Bridges.DebugReport(e,
                                        Krile.Kernel.Bridges.DebugReport.Priorities.Fatal));
                                Rectangle draw = new Rectangle(0, cpos - yoffset, parent.Width, 0);
                                if (draw.Y < 0)
                                    draw.Y = 0;
                                draw.Height = parent.Height - draw.Top;
                                g.FillRectangle(Brushes.Blue, draw);
                            }
                        }
                        if (startpoint > -1 && endpoint > -1)
                        {
                            // 初描画地点
                            DrawDTChip(parent.Statuses[startpoint], parent.Statuses[endpoint], g);
                        }
                    }
                    if (cpos < yoffset + parent.Height)
                    {
                        // draw remain area
                        Rectangle draw = new Rectangle(0, cpos - yoffset, parent.Width, 0);
                        if (draw.Y < 0)
                            draw.Y = 0;
                        draw.Height = parent.Height - draw.Top;
                        g.FillRectangle(SystemBrushes.Control, draw);

                    }
                }
            }
            catch (Exception e)
            {
                Subsystem.Debugger.AddReport(
                    new Krile.Kernel.Bridges.DebugReport(e,
                        Krile.Kernel.Bridges.DebugReport.Priorities.Fatal));
                throw;
            }
        }

        //通常状態アイテムの描画
        private void DrawNormalItem(XStatus s, int i, Graphics g, int h)
        {
            if (s == null || s.BaseStatus == null)
                return;
            //描画領域の取得
            Rectangle target = new Rectangle(0, h, parent.Width, NormalItemHeight);

            //背景の描画
            using (Brush b = new SolidBrush(GetBackColor(s, i)))
                g.FillRectangle(b, target);

            //名前描画領域の確定
            Rectangle nameArea = target;
            nameArea.Width = Core.Config.Appearance.Renderer.UserNameArea;
            using (Brush b = new SolidBrush(Color.FromArgb(180, s.BaseStatus.ParentService.GetServiceColor())))
                g.FillRectangle(b, nameArea);

            if (Core.Config.Appearance.Renderer.ShowListIcon)
            {
                if (Core.Config.Appearance.Renderer.P3StyleUserIcon)
                {
                    Rectangle iconDest = nameArea;
                    iconDest.Width = iconDest.Height * 2;
                    nameArea.X += iconDest.Right;
                    nameArea.Width -= iconDest.Right;
                    //rendering
                    if (s.BaseStatus.User.IconUri == null)
                    {
                        g.FillRectangle(Brushes.Red, iconDest);
                    }
                    else
                    {
                        if (Core.ImageManager.IsCacheExists(s.BaseStatus.User.IconUri))
                        {
                            using (var img = Core.ImageManager.GetImage(s.BaseStatus.User.IconUri, false))
                            {
                                if (img == null)
                                    g.FillRectangle(Brushes.Red, iconDest);
                                else
                                    g.DrawImage(img, iconDest, new Rectangle(0, img.Height / 4, img.Width, img.Height / 2), GraphicsUnit.Pixel);
                            }
                        }
                        else
                        {
                            Core.ImageManager.GetImageCallback(s.BaseStatus.User.IconUri, false, new Krile.Kernel.Bridges.GetImageCallbackDelegate(
                                (u, img) => { if (img != null) { img.Dispose(); img = null; } parent.Refresher.RefreshSurface(false, true); }));
                            g.FillRectangle(Brushes.White, iconDest);
                        }
                    }

                }
                else
                {
                    //アイコン画像
                    Rectangle iconDest = nameArea;
                    iconDest.Width = iconDest.Height;
                    nameArea.X += iconDest.Right;
                    nameArea.Width -= iconDest.Right;
                    iconDest.Inflate(-1, -1);
                    //アイコン画像の描画
                    if (s.BaseStatus.User.IconUri == null)
                    {
                        g.FillRectangle(Brushes.Red, iconDest);
                    }
                    else
                    {
                        if (Core.ImageManager.IsCacheExists(s.BaseStatus.User.IconUri))
                        {
                            using (var img = Core.ImageManager.GetImage(s.BaseStatus.User.IconUri, false))
                            {
                                if (img == null)
                                    g.FillRectangle(Brushes.Red, iconDest);
                                else
                                    g.DrawImage(img, iconDest);
                            }
                        }
                        else
                        {
                            Core.ImageManager.GetImageCallback(s.BaseStatus.User.IconUri, false, new Krile.Kernel.Bridges.GetImageCallbackDelegate(
                                (u, img) => { if (img != null) { img.Dispose(); img = null; } parent.Refresher.RefreshSurface(false, true); }));
                            g.FillRectangle(Brushes.White, iconDest);
                        }
                    }
                }
            }
            string name = s.BaseStatus.User.Name;
            if (Core.Config.Appearance.Renderer.ShowUserId)
                name += "@" + s.BaseStatus.User.Id;
            //ユーザー名描画
            if (Core.Config.Appearance.Renderer.UserNameCentering)
                TextRenderer.DrawText(g, name, Core.Config.Appearance.Font.Value, nameArea, s.BaseStatus.ParentService.GetServiceForeColor(),
                    TextFormatFlags.EndEllipsis | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix);
            else
                TextRenderer.DrawText(g, name, Core.Config.Appearance.Font.Value, nameArea, s.BaseStatus.ParentService.GetServiceForeColor(),
                    TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix);
            target.X += nameArea.Right;
            target.Width -= nameArea.Right;
            if (target.Width <= 0)
                return;
            //ヘッダテキスト描画
            if (!String.IsNullOrEmpty(s.BaseStatus.Text.HeaderText))
            {
                int reqWidth = TextRenderer.MeasureText(g, s.BaseStatus.Text.HeaderText, Core.Config.Appearance.Font.Value, new Size(),
                     TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter).Width;
                Rectangle htrect = target;
                htrect.Width = reqWidth;
                htrect.X += 2;
                htrect.Inflate(0, -2);
                using (var b = new SolidBrush(Core.Config.Appearance.Renderer.Colors.ListHeaderFooterColor.BackColor))
                    g.FillRectangle(b, htrect);
                TextRenderer.DrawText(g, s.BaseStatus.Text.HeaderText, Core.Config.Appearance.Font.Value, htrect,
                    Core.Config.Appearance.Renderer.Colors.ListHeaderFooterColor.ForeColor,
                    TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix);
                target.X = htrect.Right + 2;
                target.Width = parent.Width - target.X;
                if (target.Width <= 0)
                    return;
            }
            //フッタテキスト描画
            if (!String.IsNullOrEmpty(s.BaseStatus.Text.FooterText))
            {
                int reqWidth = TextRenderer.MeasureText(g, s.BaseStatus.Text.FooterText, Core.Config.Appearance.Font.Value, new Size(),
                     TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter).Width;
                if (reqWidth > target.Width - 4)
                    reqWidth = target.Width - 4;
                target.Width -= reqWidth + 4;
                Rectangle ftrect = target;
                ftrect.Width = reqWidth;
                ftrect.X = target.Right;
                ftrect.X--;
                ftrect.Inflate(0, -2);
                using (var b = new SolidBrush(Core.Config.Appearance.Renderer.Colors.ListHeaderFooterColor.BackColor))
                    g.FillRectangle(b, ftrect);
                TextRenderer.DrawText(g, s.BaseStatus.Text.FooterText, Core.Config.Appearance.Font.Value, ftrect,
                    Core.Config.Appearance.Renderer.Colors.ListHeaderFooterColor.ForeColor,
                    TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix);
                if (target.Width <= 0)
                    return;
            }
            //本文描画
            if (Core.Config.Appearance.Renderer.Detail.ShowNormalStatusWithMultiline)
            {
                TextRenderer.DrawText(g, s.BaseStatus.Text.Original, Core.Config.Appearance.Font.Value, target,
                    GetForeColor(s, i),
                    TextFormatFlags.WordBreak | TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix);
            }
            else
            {
                TextRenderer.DrawText(g, s.BaseStatus.Text.Original, Core.Config.Appearance.Font.Value, target,
                    GetForeColor(s, i),
                    TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix);
            }
        }

        private Color GetBackColor(XStatus s, int i)
        {
            if (s != null && s.BaseStatus != null)
            {
                if (s.BaseStatus.OverrideBackColor != Color.Transparent && s.BaseStatus.OverrideBackColor != Color.Empty)
                    return s.BaseStatus.OverrideBackColor;
                //自分への返信着色が有効になっている
                if ((s.Unread && Core.Config.Appearance.Renderer.Colors.ListReplyToMeNewestColor.BackEnabled) ||
                    Core.Config.Appearance.Renderer.Colors.ListReplyToMeColor.BackEnabled)
                {
                    //自分への返信かチェック
                    if (s.BaseStatus.ReplyData != null)
                    {
                        if ((s.BaseStatus.ReplyData.ReplyToUserId != null &&
                            s.BaseStatus.ParentAgent.IsMasterId(s.BaseStatus.ReplyData.ReplyToUserId)) ||
                            (s.BaseStatus.ReplyData.ReplyToId != null &&
                            s.BaseStatus.ParentAgent.Status.ServiceStatusIdExists(s.BaseStatus.ReplyData.ReplyToId) &&
                            Core.StatusManager.IdExists(s.BaseStatus.ParentAgent.Status.GetUidFromServiceStatusId(s.BaseStatus.ReplyData.ReplyToId)) &&
                            s.BaseStatus.ParentAgent.IsMasterId(Core.StatusManager.GetStatus(s.BaseStatus.ParentAgent.Status.GetUidFromServiceStatusId(s.BaseStatus.ReplyData.ReplyToId)).User.Id)))
                        {
                            //返信だった
                            if (s.Unread)
                                return Core.Config.Appearance.Renderer.Colors.ListReplyToMeNewestColor.BackColor;
                            else
                                return Core.Config.Appearance.Renderer.Colors.ListReplyToMeColor.BackColor;
                        }
                    }
                }

                if (parent.SelectedStatus != null && parent.SelectedStatus.BaseStatus != null)
                {
                    //選択ステータスはnullでない
                    if (Core.Config.Appearance.Renderer.Colors.ListReplyToSelectedColor.BackEnabled &&
                        s.BaseStatus.ReplyData != null &&
                        s.BaseStatus.ReplyData.ReplyToId != null &&
                        s.BaseStatus.ReplyData.ReplyToId == parent.SelectedStatus.BaseStatus.Id)
                        return Core.Config.Appearance.Renderer.Colors.ListReplyToSelectedColor.BackColor;

                    if (Core.Config.Appearance.Renderer.Colors.ListReplyFromSelectedColor.BackEnabled &&
                        parent.SelectedStatus.BaseStatus.ReplyData != null &&
                        parent.SelectedStatus.BaseStatus.ReplyData.ReplyToId == s.BaseStatus.Id)
                        return Core.Config.Appearance.Renderer.Colors.ListReplyFromSelectedColor.BackColor;

                    if (parent.SelectedStatus != s &&
                        parent.SelectedStatus.BaseStatus.User.Id == s.BaseStatus.User.Id &&
                        Core.Config.Appearance.Renderer.Colors.ListSelectedUserColor.BackEnabled)
                        return Core.Config.Appearance.Renderer.Colors.ListSelectedUserColor.BackColor;
                }

                if (Core.Config.Appearance.Renderer.Colors.ListMyPostColor.BackEnabled &&
                    s.BaseStatus.ParentAgent.IsMasterId(s.BaseStatus.User.Id))
                    return Core.Config.Appearance.Renderer.Colors.ListMyPostColor.BackColor;

                if (s.Unread && Core.Config.Appearance.Renderer.Colors.ListNewestColor.BackEnabled)
                    return Core.Config.Appearance.Renderer.Colors.ListNewestColor.BackColor;
            }
            return i % 2 == 0 || !Core.Config.Appearance.Renderer.Colors.ListAlternateBackgroundColor.Enabled ?
            Core.Config.Appearance.BaseColorConfig.ListBackgroundColor.Value :
            Core.Config.Appearance.Renderer.Colors.ListAlternateBackgroundColor.Value;
        }

        private Color GetForeColor(XStatus s, int i)
        {
            if (s != null && s.BaseStatus != null)
            {
                if (s.BaseStatus.OverrideForeColor != Color.Transparent && s.BaseStatus.OverrideForeColor != Color.Empty)
                    return s.BaseStatus.OverrideForeColor;
                //自分への返信着色が有効になっている
                if ((s.Unread && Core.Config.Appearance.Renderer.Colors.ListReplyToMeNewestColor.ForeEnabled) ||
                    Core.Config.Appearance.Renderer.Colors.ListReplyToMeColor.ForeEnabled)
                {
                    //自分への返信かチェック
                    if (s.BaseStatus.ReplyData != null)
                    {
                        if ((s.BaseStatus.ReplyData.ReplyToUserId != null &&
                            s.BaseStatus.ParentAgent.IsMasterId(s.BaseStatus.ReplyData.ReplyToUserId)) ||
                            (s.BaseStatus.ReplyData.ReplyToId != null &&
                            s.BaseStatus.ParentAgent.Status.ServiceStatusIdExists(s.BaseStatus.ReplyData.ReplyToId) &&
                            Core.StatusManager.IdExists(s.BaseStatus.ParentAgent.Status.GetUidFromServiceStatusId(s.BaseStatus.ReplyData.ReplyToId)) &&
                            s.BaseStatus.ParentAgent.IsMasterId(Core.StatusManager.GetStatus(s.BaseStatus.ParentAgent.Status.GetUidFromServiceStatusId(s.BaseStatus.ReplyData.ReplyToId)).User.Id)))
                        {
                            //返信だった
                            if (s.Unread)
                                return Core.Config.Appearance.Renderer.Colors.ListReplyToMeNewestColor.ForeColor;
                            else
                                return Core.Config.Appearance.Renderer.Colors.ListReplyToMeColor.ForeColor;
                        }
                    }
                }

                if (parent.SelectedStatus != null && parent.SelectedStatus.BaseStatus != null)
                {
                    //選択ステータスはnullでない
                    if (Core.Config.Appearance.Renderer.Colors.ListReplyToSelectedColor.ForeEnabled &&
                        s.BaseStatus.ReplyData != null &&
                        s.BaseStatus.ReplyData.ReplyToId != null &&
                        s.BaseStatus.ReplyData.ReplyToId == parent.SelectedStatus.BaseStatus.Id)
                        return Core.Config.Appearance.Renderer.Colors.ListReplyToSelectedColor.ForeColor;

                    if (Core.Config.Appearance.Renderer.Colors.ListReplyFromSelectedColor.ForeEnabled &&
                        parent.SelectedStatus.BaseStatus.ReplyData != null &&
                        parent.SelectedStatus.BaseStatus.ReplyData.ReplyToId == s.BaseStatus.Id)
                        return Core.Config.Appearance.Renderer.Colors.ListReplyFromSelectedColor.ForeColor;

                    if (parent.SelectedStatus != s &&
                        parent.SelectedStatus.BaseStatus.User.Id == s.BaseStatus.User.Id &&
                        Core.Config.Appearance.Renderer.Colors.ListSelectedUserColor.ForeEnabled)
                        return Core.Config.Appearance.Renderer.Colors.ListSelectedUserColor.ForeColor;
                }

                if (Core.Config.Appearance.Renderer.Colors.ListMyPostColor.ForeEnabled &&
                    s.BaseStatus.ParentAgent.IsMasterId(s.BaseStatus.User.Id))
                    return Core.Config.Appearance.Renderer.Colors.ListMyPostColor.ForeColor;

                if (s.Unread && Core.Config.Appearance.Renderer.Colors.ListNewestColor.ForeEnabled)
                    return Core.Config.Appearance.Renderer.Colors.ListNewestColor.ForeColor;
            }
            return Core.Config.Appearance.BaseColorConfig.ForeColor.Value;
        }

        //選択状態アイテムの描画
        private void DrawSelectedItem(XStatus s, Graphics g, int h)
        {
            if (s.Unread && Core.Config.Timeline.RemoveNewestFlagByRead)
                s.Unread = false;
            //描画領域の初期化
            Rectangle target = new Rectangle(0, h, parent.Width - 1, SelectedItemHeight - 1);
            //枠の描画
            using (var p = new Pen(s.BaseStatus.ParentService.GetServiceColor()))
                g.DrawRectangle(p, target);
            target.Width++;
            target.Height++;
            target.Inflate(-1, -1);
            //背景描画
            using (var b = new SolidBrush(Core.Config.Appearance.BaseColorConfig.ListBackgroundColor.Value))
                g.FillRectangle(b, target);
            //ハイライトの描画
            Rectangle highlight = target;
            highlight.Width /= 4;
            if (highlight.Width < highlight.Height)
                highlight.Width = highlight.Height;
            highlight.X = target.Right - highlight.Width;
            using (Brush b = new LinearGradientBrush(highlight, GetBackColor(s, 0), Color.Transparent, 225f))
                g.FillRectangle(b, highlight);

            if (Core.Config.Appearance.Renderer.ShowMainIcon)
            {
                Rectangle nameArea = target;
                nameArea.Width = Core.Config.Appearance.Renderer.UserNameArea - 1;
                //名前欄グラデーション描画
                using (var b = new LinearGradientBrush(nameArea, s.BaseStatus.ParentService.GetServiceSecondaryColor(), Core.Config.Appearance.BaseColorConfig.ListBackgroundColor.Value, 0f))
                {
                    b.GammaCorrection = true;
                    g.FillRectangle(b, nameArea);
                }
                //アイコン画像転送先確定
                Rectangle imageDest = nameArea;
                if (imageDest.Width < imageDest.Height)
                {
                    imageDest.Height = imageDest.Width;
                }
                else
                {
                    imageDest.Width = imageDest.Height;
                    imageDest.X = (nameArea.Width - imageDest.Width) / 2;
                }
                imageDest.Inflate(-3, -3);
                //丸みのある矩形でクリップ
                var path = Imported.Snippets.Drawing.PathExtensions.GetCircularRectanglePathFromRectangle(imageDest, new Size(4, 4));
                var origClip = g.ClipBounds;
                g.SetClip(path);
                //ユーザーアイコン描画
                if (s.BaseStatus.User.IconUri == null)
                {
                    g.DrawImage(Properties.Resources.fail, imageDest);
                }
                else if (Core.ImageManager.IsCacheExists(s.BaseStatus.User.IconUri))
                {
                    using (var img = Core.ImageManager.GetImage(s.BaseStatus.User.IconUri, false))
                    {
                        if (img == null)
                            g.DrawImage(Properties.Resources.fail, imageDest);
                        else
                            g.DrawImage(img, imageDest);
                    }
                }
                else
                {
                    Core.ImageManager.GetImageCallback(s.BaseStatus.User.IconUri, false, new Krile.Kernel.Bridges.GetImageCallbackDelegate(
                        (u, img) => { if (img != null) { img.Dispose(); img = null; } parent.Refresher.RefreshSurface(false, true); }));
                    g.DrawImage(Properties.Resources.loading, imageDest);
                }
                //クリップ解除
                g.SetClip(origClip);
                //サービスアイコン描画
                Rectangle iconDest = new Rectangle(imageDest.Right, imageDest.Bottom, 16, 16);
                iconDest.X -= 14;
                iconDest.Y -= 14;
                target.X += nameArea.Width;
                target.Width -= nameArea.Width;
                g.DrawImage(s.BaseStatus.ParentService.ServiceIcon, iconDest);
                lock (soplocker)
                {
                    //アイコンクリック抽出
                    var isop = new SingleOperation()
                    {
                        Description = Lang.Renderer.ClickToExtractService,
                        HitArea = iconDest,
                        MouseEffect = true
                    };
                    isop.OnMouseClicked += new Action<ChainMouseEventArgs>((e) =>
                    {
                        if (e.Button == MouseButtons.Left)
                            Data.DefaultKeyAssigns.ExtractThisService(this.parent.GetParentForm(), s.BaseStatus, Keys.None);
                        else
                            e.SetThrowSub();
                    });
                    isop.OnMouseMoved += new Action<ChainMouseEventArgs>((e) =>
                    {
                        if (e.Button == MouseButtons.Left &&
                            !isop.HitTest(e.Location))
                        {
                            var dd = parent.DoDragDrop(
                                new XTab.XTabDroppable()
                                {
                                    OnDroppableThis = new Func<Krile.Forms.MainForm.Controls.XTab.XTab, bool>((f) =>
                                    {
                                        var kxt = f as KrileXTab;
                                        if (kxt == null || kxt.IsWindowTab)
                                            return false;
                                        else
                                            return true;
                                    }),
                                    OnDropToAppend = new Action<Krile.Forms.MainForm.Controls.XTab.XTab, Krile.Forms.MainForm.Controls.XTab.XTabItem>((p, item) =>
                                    {
                                        var tl = item as XTab.XTabTLItem;
                                        if (tl == null)
                                            return;
                                        var sd = tl.TabData.ContainSearchData;
                                        if (sd.UseOrMatch)
                                        {
                                            List<SearchDataBase> sdbs = new List<SearchDataBase>(sd.SearchDatas);
                                            sdbs.Add(new Data.ServiceIdSearchData(s.BaseStatus.ParentService.Id));
                                            tl.TabData.ContainSearchData.SearchDatas = sdbs.ToArray();
                                        }
                                        else
                                        {
                                            List<SearchDataBase> sdbs = new List<SearchDataBase>();
                                            sdbs.Add(sd);
                                            sdbs.Add(new Data.ServiceIdSearchData(s.BaseStatus.ParentService.Id));
                                            tl.TabData.ContainSearchData = new SearchDataCluster(sdbs.ToArray()) { UseOrMatch = true };
                                        }
                                        tl.TabData.InitTabData();
                                        var pf = parent.GetParentForm();
                                        if (pf != null)
                                        {
                                            pf.UpdateTimeline();
                                            pf.TabControl.Refresh();
                                        }
                                    }),
                                    OnDropToNew = new Action<Krile.Forms.MainForm.Controls.XTab.XTab>((p) =>
                                    {
                                        Data.DefaultKeyAssigns.ExtractThisService(parent.GetParentForm(), s.BaseStatus);
                                    })
                                }, DragDropEffects.Copy);
                        }
                    });

                    sopclusters.Add(isop);
                    var usop = new SingleOperation()
                    {
                        Description = Lang.Renderer.ClickToExtractUser,
                        HitArea = imageDest,
                        MouseEffect = true
                    };
                    usop.OnMouseClicked += new Action<ChainMouseEventArgs>((e) =>
                    {
                        if (e.Button == MouseButtons.Left)
                            Data.DefaultKeyAssigns.ExtractThisUser(this.parent.GetParentForm(), s.BaseStatus, Keys.None);
                        else
                            e.SetThrowSub();
                    });
                    usop.OnMouseMoved += new Action<ChainMouseEventArgs>((e) =>
                    {
                        if (e.Button == MouseButtons.Left &&
                            !usop.HitTest(e.Location))
                        {
                            var dd = parent.DoDragDrop(
                                new XTab.XTabDroppable()
                                {
                                    OnDroppableThis = new Func<Krile.Forms.MainForm.Controls.XTab.XTab, bool>((f) =>
                                    {
                                        var kxt = f as KrileXTab;
                                        if (kxt == null || kxt.IsWindowTab)
                                            return false;
                                        else
                                            return true;
                                    }),
                                    OnDropToAppend = new Action<Krile.Forms.MainForm.Controls.XTab.XTab, Krile.Forms.MainForm.Controls.XTab.XTabItem>((p, item) =>
                                    {
                                        var tl = item as XTab.XTabTLItem;
                                        if (tl == null)
                                            return;
                                        var sd = tl.TabData.ContainSearchData;
                                        if (sd == null)
                                        {
                                            sd = new SearchDataCluster() { UseOrMatch = true };
                                            sd.SearchDatas = new SearchDataBase[] { new Data.UserIdSearchData(s.BaseStatus.User.Id) };
                                            tl.TabData.ContainSearchData = sd;
                                        }
                                        else if (sd.UseOrMatch)
                                        {
                                            List<SearchDataBase> sdbs = new List<SearchDataBase>();
                                            if (sd.SearchDatas != null)
                                                sdbs.AddRange(sd.SearchDatas);
                                            sdbs.Add(new Data.UserIdSearchData(s.BaseStatus.User.Id));
                                            tl.TabData.ContainSearchData.SearchDatas = sdbs.ToArray();
                                        }
                                        else
                                        {
                                            List<SearchDataBase> sdbs = new List<SearchDataBase>();
                                            if (sd != null)
                                                sdbs.Add(sd);
                                            sdbs.Add(new Data.UserIdSearchData(s.BaseStatus.User.Id));
                                            tl.TabData.ContainSearchData = new SearchDataCluster(sdbs.ToArray()) { UseOrMatch = true };
                                        }
                                        tl.TabData.InitTabData();
                                        var pf = parent.GetParentForm();
                                        if (pf != null)
                                        {
                                            pf.UpdateTimeline();
                                            pf.TabControl.Refresh();
                                        }
                                    }),
                                    OnDropToNew = new Action<Krile.Forms.MainForm.Controls.XTab.XTab>((p) =>
                                    {
                                        var sd = new SearchDataCluster();
                                        sd.SearchDatas = new SearchDataBase[] { new Data.UserIdSearchData(s.BaseStatus.User.Id) };
                                        var pf = parent.GetParentForm();
                                        if (pf != null)
                                        {
                                            pf.CreateTab("User:" + s.BaseStatus.User.Name, sd, false, new[] { s.BaseStatus.ParentService.Id });
                                            pf.TabControl.Refresh();
                                        }
                                    })
                                }, DragDropEffects.Copy);
                        }
                    });
                    sopclusters.Add(usop);
                }
            }
            //描画の実装
            Point loc = target.Location;
            if (Core.Config.Appearance.Renderer.NameAbove)
                target.Y += Core.Config.Advanced.SelectedListNameCommandHeight;
            else
                loc.Y = target.Bottom - Core.Config.Advanced.SelectedListNameCommandHeight;
            DrawNamebarElements(s, g, new Rectangle(loc, new Size(target.Width, Core.Config.Advanced.SelectedListNameCommandHeight)));
            target.Height -= Core.Config.Advanced.SelectedListNameCommandHeight;
            DrawBody(s, g, target);
        }

        private void DrawDTChip(XStatus begin, XStatus end, Graphics g)
        {
            string showstr = begin.BaseStatus.CreatedAt.ToString("MM/dd HH:mm:ss") + " - ";
            if (begin.BaseStatus.CreatedAt.Date.CompareTo(end.BaseStatus.CreatedAt.Date) == 0)
                showstr += end.BaseStatus.CreatedAt.ToString("HH:mm:ss");
            else
                showstr += end.BaseStatus.CreatedAt.ToString("MM/dd HH:mm:ss");

            var drawarea = TextRenderer.MeasureText(g, showstr, parent.Font);
            drawarea.Width += 18;
            drawarea.Height += 18;
            var tgrect = new Rectangle(new Point(parent.Width - drawarea.Width + 6, -6), drawarea);
            var path = Imported.Snippets.Drawing.PathExtensions.GetCircularRectanglePathFromRectangle(
                tgrect, new Size(10, 10));
            using (var b = new SolidBrush(Color.FromArgb(200, Color.Black)))
            {
                g.FillPath(b, path);
            }
            tgrect.Width -= 6;
            tgrect.Y = 0;
            tgrect.Height -= 6;
            TextRenderer.DrawText(g, showstr, parent.Font, tgrect, Color.White, TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
        }

        private void DrawNamebarElements(XStatus s, Graphics g, Rectangle bound)
        {
            //背景の描画
            using (var b = new SolidBrush(Color.FromArgb(120, s.BaseStatus.ParentService.GetServiceSecondaryColor())))
                g.FillRectangle(b, bound);
            //マージンの確保
            bound.Inflate(-Core.Config.Appearance.Renderer.Detail.TextRenderingMargin, -Core.Config.Appearance.Renderer.Detail.TextRenderingMargin);
            //ユーザー名の描画準備
            string unm = s.BaseStatus.User.Name + "(@" + s.BaseStatus.User.Id + ")";
            int w = TextRenderer.MeasureText(g, unm, Core.Config.Appearance.Font.Value, new Size(),
                 TextFormatFlags.Left | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine).Width;
            if (s.BaseStatus.Commands != null && s.BaseStatus.Commands.Length > 0 && w > bound.Width - 10)
                w = bound.Width - 10;
            if (w < 0)
                w = 0;
            //オペレーション追加
            var sop = new SingleOperation()
            {
                Description = Lang.Renderer.ClickToShowUserpage,
                HitArea = new Rectangle(bound.Location, new Size(w, bound.Height)),
                MouseEffect = true
            };
            sop.OnMouseClicked += new Action<ChainMouseEventArgs>((e) =>
            {
                if (e.Button == MouseButtons.Left)
                    Subsystem.CommonOperation.CallBrowser(s.BaseStatus.User.ProfileUri.OriginalString);
                else
                    e.SetThrowSub();
            });
            sopclusters.Add(sop);
            //描画実行
            using (var f = new Font(Core.Config.Appearance.Font.Value, FontStyle.Underline))
                TextRenderer.DrawText(g, unm, f, bound, Core.Config.Appearance.BaseColorConfig.LinkTextColor.Value,
                     TextFormatFlags.Left | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis);
            //コマンド描画
            bound.X += w;
            bound.Width -= w;
            int rendered;
            var d = s.BaseStatus.Commands.Draw(g, bound.Location, bound.Width, out rendered);
            if (d != null)
                sopclusters.Add(d);
            bound.Width -= rendered;
            if (bound.Width == 0) return;
            //投稿時刻描画準備
            TextRenderer.DrawText(
                g, s.BaseStatus.CreatedAt.ToString(Core.Config.Appearance.Renderer.Detail.DateTimeFormat),
                Core.Config.Appearance.Font.Value, bound, Core.Config.Appearance.BaseColorConfig.ForeSecondaryColor.Value,
                TextFormatFlags.Left | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis);
        }

        ContextMenuStrip replycms = null;
        object soplocker = new object();
        List<IMouseControllable> sopclusters = new List<IMouseControllable>();
        //本体の描画
        private void DrawBody(XStatus s, Graphics g, Rectangle bound)
        {
            if (s.BaseStatus.ReplyData != null)
            {
                //返信部分の描画

                //描画領域の確定
                Rectangle rdArea = bound;
                rdArea.Height = Core.Config.Appearance.Renderer.Detail.NormalListHeight;
                bound.Y += rdArea.Height;
                bound.Height -= rdArea.Height;

                //角丸矩形で塗りつぶし
                var path = Imported.Snippets.Drawing.PathExtensions.GetCircularRectanglePathFromRectangle(rdArea, new Size(4, 4));
                using (var b = new SolidBrush(
                                        s.BaseStatus.ReplyData.ReplyToId != null && s.BaseStatus.ParentAgent.Status.ServiceStatusIdExists(s.BaseStatus.ReplyData.ReplyToId) ?
                                        Core.Config.Appearance.Renderer.Colors.ListReplyColor.BackColor :
                                        Core.Config.Appearance.Renderer.Colors.ListReplyNotfoundColor.BackColor))
                    g.FillPath(b, path);

                //当たり判定用に取っておく
                Rectangle orgrdArea = rdArea;
                //マージンの適用
                rdArea.Inflate(-Core.Config.Appearance.Renderer.Detail.TextRenderingMargin, -Core.Config.Appearance.Renderer.Detail.TextRenderingMargin);
                //テキストのフォーマット
                string showtext = s.BaseStatus.ReplyData.ReplyToUserName;
                if (showtext == null)
                    showtext = "@" + s.BaseStatus.ReplyData.ReplyToUserId;
                showtext = ">" + showtext;
                if (s.BaseStatus.ReplyData.ReplyToText != null)
                    showtext += ":" + s.BaseStatus.ReplyData.ReplyToText;
                else
                    showtext += ":" + "(no document)";
                //描画(該当返信先へジャンプできるかどうかで色を変更する)
                TextRenderer.DrawText(g, showtext, Core.Config.Appearance.Font.Value, rdArea,
                    s.BaseStatus.ReplyData.ReplyToId != null && s.BaseStatus.ParentAgent.Status.ServiceStatusIdExists(s.BaseStatus.ReplyData.ReplyToId) ?
                    Core.Config.Appearance.Renderer.Colors.ListReplyColor.ForeColor :
                    Core.Config.Appearance.Renderer.Colors.ListReplyNotfoundColor.ForeColor,
                TextFormatFlags.EndEllipsis | TextFormatFlags.Left | TextFormatFlags.NoPrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);


                var sop = new SingleOperation()
                {
                    Description = showtext,
                    HitArea = orgrdArea,
                    MouseEffect = true
                };

                sop.OnMouseClicked += new Action<ChainMouseEventArgs>((e) =>
                {
                    if (e.Button == MouseButtons.Left)
                    {
                        var uid = s.BaseStatus.ReplyData.ReplyOnClick();
                        if (uid == 0) return;
                        var pop = new Popup(parent.GetParentForm(), uid);
                        pop.Size = new Size(parent.Width, 30);
                        pop.Location = parent.PointToScreen(new Point(0, bound.Location.Y));
                        parent.FindForm().AddOwnedForm(pop);
                        Core.FormManager.RegisterPopup(pop);
                        pop.Show();
                    }
                    else if (e.Button == MouseButtons.Right)
                    {
                        replycms = parent.CreateReplyContextMenu();
                        replycms.Show(parent, e.Location);
                    }
                    else
                    {
                        e.SetThrowSub();
                    }
                });
                sopclusters.Add(sop);
            }
            bound.Inflate(-Core.Config.Appearance.Renderer.Detail.TextRenderingMargin, -Core.Config.Appearance.Renderer.Detail.TextRenderingMargin);
            if (!compman.Compositable || compman.IsMsgobjsValid(s.BaseStatus.Text.MessageObjects))
                UpdateComposite();
            compman.Draw(g, bound.Location, parent.GetParentForm());
        }

        //なんかやるクラス
        class SingleOperation : IMouseControllable
        {
            internal string Description { get; set; }
            internal Rectangle HitArea { get; set; }
            internal bool MouseEffect { get; set; }

            public Krile.Kernel.Bridges.IForm ParentForm { get; set; }

            public event Action<ChainMouseEventArgs> OnMouseClicked;
            public event Action<ChainMouseEventArgs> OnMouseEntered;
            public event Action<ChainMouseEventArgs> OnMouseLeaved;
            public event Action<ChainMouseEventArgs> OnMouseMoved;
            public string HelptipText
            {
                get { return Description; }
            }

            public bool HitTest(Point position)
            {
                return HitArea.HitTest(position);
            }

            public void OnMouseClick(object sender, ChainMouseEventArgs e)
            {
                if (OnMouseClicked != null)
                    OnMouseClicked.Invoke(e);
                else
                    e.SetThrowSub();
            }

            public void OnMouseDoubleClick(object sender, ChainMouseEventArgs e)
            {
                OnMouseClick(sender, e);
            }

            public void OnMouseDown(object sender, ChainMouseEventArgs e)
            {
                e.SetThrowSub();
            }

            public void OnMouseEnter(object sender, ChainMouseEventArgs e)
            {
                if (MouseEffect)
                {
                    if (ParentForm != null)
                        ParentForm.SetCursor(Cursors.Hand);
                }
                if (OnMouseEntered != null)
                    OnMouseEntered.Invoke(e);
                else if (!MouseEffect)
                    e.SetThrowSub();
            }

            public void OnMouseLeave(object sender, ChainMouseEventArgs e)
            {
                if (MouseEffect)
                {
                    if (ParentForm != null)
                        ParentForm.SetCursor(Cursors.Default);
                }
                if (OnMouseLeaved != null)
                    OnMouseLeaved.Invoke(e);
                else if (!MouseEffect)
                    e.SetThrowSub();
            }

            public void OnMouseMove(object sender, ChainMouseEventArgs e)
            {
                if (OnMouseMoved != null)
                    OnMouseMoved.Invoke(e);
                else
                    e.SetThrowSub();
            }

            public void OnMouseOver(object sender, ChainMouseEventArgs e)
            {
                e.SetThrowSub();
            }

            public void OnMouseUp(object sender, ChainMouseEventArgs e)
            {
                e.SetThrowSub();
            }
        }
    }
}